namespace Yaaf.WP7.NativeAccess.IO.Internals.AllDevices
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.IO.IsolatedStorage;
    using System.Linq;

    using Yaaf.WP7.NativeAccess.ComWrapper.AllDevices.COM_NativeIO;

    using Directory = Yaaf.WP7.NativeAccess.ComWrapper.AllDevices.COM_NativeIO.Directory;
    using FileAccess = System.IO.FileAccess;
    using FileAttributes = Yaaf.WP7.NativeAccess.ComWrapper.AllDevices.COM_NativeIO.FileAttributes;
    using FileShare = Yaaf.WP7.NativeAccess.ComWrapper.AllDevices.COM_NativeIO.FileShare;

    public class FileSystemAccess : LimitedFileSystemAccess
    {
        public FileSystemAccess()
        {
            ComWrapper.AllDevices.COM_NativeIO.NativeIODllInterop.Initialize();
        }

        public override Stream OpenFile(string path, FileMode mode, FileAccess access)
        {
            if (access == FileAccess.Read)
            {
                CreationDisposition creationDisposition = GetCreationDispositionFromFileMode(mode);
                return ComWrapper.AllDevices.COM_NativeIO.File.Open(
                    path,
                    ComWrapper.AllDevices.COM_NativeIO.FileAccess.Read,
                    FileShare.None,
                    creationDisposition);
            }
            else
            {
                System.IO.IsolatedStorage.IsolatedStorageFile file = IsolatedStorageFile.GetUserStoreForApplication();
                file.CreateDirectory(DeviceInfo.TempDir);

                var fullTempDir = PathInfo.ToGeneral(DeviceInfo.TempDir);
                var fileName = System.IO.Path.GetFileName(path);
                var fullTempFileName = System.IO.Path.Combine(fullTempDir, fileName);
                var tempStoragePath = System.IO.Path.Combine(DeviceInfo.TempDir, fileName);
                var pathInfo = GetPathInformation(path);

                if (pathInfo == PathInformation.File)
                {
                    CopyFile(path, fullTempFileName, true);
                }
                else
                {
                    IsolatedStorageFile isolated = IsolatedStorageFile.GetUserStoreForApplication();

                    if (isolated.FileExists(tempStoragePath))
                    {
                        isolated.DeleteFile(tempStoragePath);
                    }
                }

                return new StreamWrapper(file.OpenFile(tempStoragePath, mode, access), path, fullTempFileName);
            }
        }

        private CreationDisposition GetCreationDispositionFromFileMode(FileMode mode)
        {
            switch (mode)
            {
                case FileMode.CreateNew:
                    return CreationDisposition.New;
                    break;
                case FileMode.Create:
                    return CreationDisposition.CreateAlways;
                    break;
                case FileMode.Open:
                   return CreationDisposition.OpenExisting;
                    break;
                case FileMode.OpenOrCreate:
                    return CreationDisposition.OpenAlways;
                    break;
                case FileMode.Truncate:
                    return CreationDisposition.TruncateExisting;
                    break;
                //case FileMode.Append:

                //    break;
                default:
                    throw new ArgumentOutOfRangeException("mode");
            }
        }

        //public bool ExistsFile(string path)
        //{
        //    throw new NotImplementedException();
        //}

        //public void MoveFile(string path, string dest, bool overwrite)
        //{
        //    throw new NotImplementedException();
        //}

        //public void DeleteFile(string path)
        //{
        //    throw new NotImplementedException();
        //}

        public override void CopyFile(string source, string dest, bool overwrite)
        {
            ComWrapper.AllDevices.COM_NativeIO.File.Copy(source, dest, !overwrite);
        }

        public override long GetFileSize(string fullName)
        {
            var first = Enumerable.ToArray<WIN32_FIND_DATA>(Directory.buffer.Values.Where(f => f.cFileName == fullName));
            if (first.Length == 1)
            {
                return first[0].GetSize();
            }

            var info = new IO.FileInfo(fullName);
            var parent = info.Parent;
            var entries = IO.Directory.GetFileSystemEntries(parent.FullName, info.Name, false).ToArray();
            if (entries.Length == 1)
            {
                return entries[0].Size;
            }
            //parent.FilterFiles(info.Name)

            try
            {
                using (var file = ComWrapper.AllDevices.COM_NativeIO.File.Open(
                    fullName,
                    ComWrapper.AllDevices.COM_NativeIO.FileAccess.Read,
                    FileShare.None,
                    CreationDisposition.OpenExisting))
                {
                    return file.Length;
                }
            }
            catch (IOException e)
            {
                throw new UnauthorizedAccessException("no access to the given file!", e);
            }
        }

        //public bool ExistsDirectory(string fullName)
        //{
        //    throw new NotImplementedException();
        //}

        //public PathInformation GetPathInformation(string path)
        //{
        //    throw new NotImplementedException();
        //}

        public override IEnumerable<FileSystemEntryStruct> EnumerateFileSystemEntries(string path, bool includeFiles, bool includeDirectories, string search, bool fullPath)
        {
            if (string.IsNullOrEmpty(search))
            {
                search = "*";
            }

            if (!includeDirectories && !includeFiles)
            {
                return new FileSystemEntryStruct[0];
            }

            if (path == "\\")
            {
                List<FileSystemEntryStruct> list=new List<FileSystemEntryStruct>();
                if (includeDirectories)
                {
                    list.Add(new FileSystemEntryStruct("Windows", false));
                }

                if (fullPath)
                {
                    Func<FileSystemEntryStruct, FileSystemEntryStruct> toFullPath = s =>
                        {
                            var newPath = Path.Combine(path, s.Name);
                            if (s.IsFile.HasValue)
                            {
                                return new FileSystemEntryStruct(newPath, s.IsFile.Value);
                            }
                            else
                            {
                                return new FileSystemEntryStruct(newPath);
                            }
                        };
                    list = list.Select(toFullPath).ToList();
                }

                return list;
            }
            var entries = Directory.EnumerateDirectory(path, search, fullPath);
            
            Func<WIN32_FIND_DATA, string > fileNameSelector;
            if (fullPath)
            {
                fileNameSelector = d => Path.Combine(path, d.cFileName);
            }
            else
            {
                fileNameSelector = d => d.cFileName;
            } 
            
            var tempResult = entries.Select(
                d =>
                    {
                        bool isFile = (d.dwFileAttributes & FileAttributes.Directory) != FileAttributes.Directory;
                        long size = -1;
                        if (isFile)
                        {
                            size = d.GetSize();
                        }
                        return new FileSystemEntryStruct(fileNameSelector(d), isFile, size);

                    });
            
            if (includeDirectories && includeFiles)
            {
                return tempResult;
            }

            if (includeDirectories)
            {
                return tempResult.Where(f => !f.IsFile.Value);
            }

            if (includeFiles)
            {
                return tempResult.Where(f => f.IsFile.Value);
            }

            return new FileSystemEntryStruct[0];
        }

        

        //public void CreateDirectory(string fullName)
        //{
        //    throw new NotImplementedException();
        //}


        //public void DeleteDirectory(string fullName, bool recursive)
        //{
        //    throw new NotImplementedException();
        //}

        //public void ExecuteFile(string fullName)
        //{
        //    throw new NotImplementedException();
        //}
    }
}